home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™ 1987-1994 / MacHack™ '87 / Source ƒ.sea / Source ƒ / emacs source ƒ / SEARCH.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-28  |  14.3 KB  |  606 lines  |  [TEXT/MARC]

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions. There are no special characters in the search
  4.  * strings. Probably should have a regular expression search, or something
  5.  * like that.
  6.  *
  7.  */
  8.  
  9. #include        <stdio.h>
  10. #include    "estruct.h"
  11. #include        "edef.h"
  12.  
  13. /*
  14.  * Search forward. Get a search string from the user, and search, beginning at
  15.  * ".", for the string. If found, reset the "." to be just after the match
  16.  * string, and [perhaps] repaint the display. Bound to "C-S".
  17.  */
  18.  
  19. /*    string search input parameters    */
  20.  
  21. #define    PTBEG    1    /* leave the point at the beginning on search */
  22. #define    PTEND    2    /* leave the point at the end on search */
  23.  
  24. forwsearch(f, n)
  25.  
  26. {
  27.     register int status;
  28.  
  29.     /* resolve the repeat count */
  30.     if (n == 0)
  31.         n = 1;
  32.     if (n < 1)    /* search backwards */
  33.         return(backsearch(f, -n));
  34.  
  35.     /* ask the user for the text of a pattern */
  36.     if ((status = readpattern("Search")) != TRUE)
  37.         return(status);
  38.  
  39.     /* search for the pattern */
  40.     while (n-- > 0) {
  41.         if ((status = forscan(&pat[0],PTEND)) == FALSE)
  42.             break;
  43.     }
  44.  
  45.     /* and complain if not there */
  46.     if (status == FALSE)
  47.         mlwrite("Not found");
  48.     return(status);
  49. }
  50.  
  51. forwhunt(f, n)
  52.  
  53. {
  54.     register int status;
  55.  
  56.     /* resolve the repeat count */
  57.     if (n == 0)
  58.         n = 1;
  59.     if (n < 1)    /* search backwards */
  60.         return(backhunt(f, -n));
  61.  
  62.     /* Make sure a pattern exists */
  63.     if (pat[0] == 0) {
  64.         mlwrite("No pattern set");
  65.         return(FALSE);
  66.     }
  67.  
  68.     /* search for the pattern */
  69.     while (n-- > 0) {
  70.         if ((status = forscan(&pat[0],PTEND)) == FALSE)
  71.             break;
  72.     }
  73.  
  74.     /* and complain if not there */
  75.     if (status == FALSE)
  76.         mlwrite("Not found");
  77.     return(status);
  78. }
  79.  
  80. /*
  81.  * Reverse search. Get a search string from the user, and search, starting at
  82.  * "." and proceeding toward the front of the buffer. If found "." is left
  83.  * pointing at the first character of the pattern [the last character that was
  84.  * matched]. Bound to "C-R".
  85.  */
  86. backsearch(f, n)
  87.  
  88. {
  89.     register int s;
  90.  
  91.     /* resolve null and negative arguments */
  92.     if (n == 0)
  93.         n = 1;
  94.     if (n < 1)
  95.         return(forwsearch(f, -n));
  96.  
  97.     /* get a pattern to search */
  98.     if ((s = readpattern("Reverse search")) != TRUE)
  99.         return(s);
  100.  
  101.     /* and go search for it */
  102.     bsearch(f,n);
  103.     return(TRUE);
  104. }
  105.  
  106. backhunt(f, n)    /* hunt backward for the last search string entered */
  107.  
  108. {
  109.     /* resolve null and negative arguments */
  110.     if (n == 0)
  111.         n = 1;
  112.     if (n < 1)
  113.         return(forwhunt(f, -n));
  114.  
  115.     /* Make sure a pattern exists */
  116.     if (pat[0] == 0) {
  117.         mlwrite("No pattern set");
  118.         return(FALSE);
  119.     }
  120.  
  121.     /* and go search for it */
  122.     bsearch(f,n);
  123.     return(TRUE);
  124. }
  125.  
  126. bsearch(f, n)
  127.  
  128. {
  129.     register LINE *clp;
  130.     register int cbo;
  131.     register LINE *tlp;
  132.     register int tbo;
  133.     register int c;
  134.     register char *epp;
  135.     register char *pp;
  136.  
  137.     /* find a pointer to the end of the pattern */
  138.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  139.         ;
  140.  
  141.     /* make local copies of the starting location */
  142.     clp = curwp->w_dotp;
  143.     cbo = curwp->w_doto;
  144.  
  145.     while (n-- > 0) {
  146.         for (;;) {
  147.             /* if we are at the beginning of the line, wrap back around */
  148.             if (cbo == 0) {
  149.                 clp = lback(clp);
  150.  
  151.                 if (clp == curbp->b_linep) {
  152.                     mlwrite("Not found");
  153.                     return(FALSE);
  154.                 }
  155.  
  156.                 cbo = llength(clp)+1;
  157.             }
  158.  
  159.             /* fake the <NL> at the end of a line */
  160.             if (--cbo == llength(clp))
  161.                 c = '\n';
  162.             else
  163.                 c = lgetc(clp, cbo);
  164.  
  165.             /* check for a match against the end of the pattern */
  166.             if (eq(c, *epp) != FALSE) {
  167.                 tlp = clp;
  168.                 tbo = cbo;
  169.                 pp  = epp;
  170.  
  171.                 /* scanning backwards through the rest of the
  172.                    pattern looking for a match            */
  173.                 while (pp != &pat[0]) {
  174.                     /* wrap across a line break */
  175.                     if (tbo == 0) {
  176.                         tlp = lback(tlp);
  177.                         if (tlp == curbp->b_linep)
  178.                             goto fail;
  179.  
  180.                         tbo = llength(tlp)+1;
  181.                     }
  182.  
  183.                     /* fake the <NL> */
  184.                     if (--tbo == llength(tlp))
  185.                         c = '\n';
  186.                     else
  187.                         c = lgetc(tlp, tbo);
  188.  
  189.                     if (eq(c, *--pp) == FALSE)
  190.                         goto fail;
  191.                 }
  192.  
  193.                 /* A Match!  reset the current cursor */
  194.                 curwp->w_dotp  = tlp;
  195.                 curwp->w_doto  = tbo;
  196.                 curwp->w_flag |= WFMOVE;
  197.                 goto next;
  198.             }
  199. fail:;
  200.         }
  201. next:;
  202.     }
  203.     return(TRUE);
  204. }
  205.  
  206. /*
  207.  * Compare two characters. The "bc" comes from the buffer. It has it's case
  208.  * folded out. The "pc" is from the pattern.
  209.  */
  210. eq(bc, pc)
  211.     int bc;
  212.     int pc;
  213.  
  214. {
  215.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
  216.         if (bc>='a' && bc<='z')
  217.             bc -= 0x20;
  218.  
  219.         if (pc>='a' && pc<='z')
  220.             pc -= 0x20;
  221.     }
  222.  
  223.     if (bc == pc)
  224.         return(TRUE);
  225.  
  226.     return(FALSE);
  227. }
  228.  
  229. /*
  230.  * Read a pattern. Stash it in the external variable "pat". The "pat" is not
  231.  * updated if the user types in an empty line. If the user typed an empty line,
  232.  * and there is no old pattern, it is an error. Display the old pattern, in the
  233.  * style of Jeff Lomicka. There is some do-it-yourself control expansion.
  234.  * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
  235.  * the search string.
  236.  */
  237. readpattern(prompt)
  238.  
  239. char *prompt;
  240.  
  241. {
  242.     register int s;
  243.     char tpat[NPAT+20];
  244.  
  245.     strcpy(tpat, prompt);    /* copy prompt to output string */
  246.     strcat(tpat, " [");    /* build new prompt string */
  247.     expandp(&pat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  248.     strcat(tpat, "]<META>: ");
  249.  
  250.     s = mlreplyt(tpat, tpat, NPAT, metac);    /* Read pattern */
  251.  
  252.     if (s == TRUE)                /* Specified */
  253.         strcpy(pat, tpat);
  254.     else if (s == FALSE && pat[0] != 0)    /* CR, but old one */
  255.         s = TRUE;
  256.  
  257.     return(s);
  258. }
  259.  
  260. sreplace(f, n)    /*    Search and replace (ESC-R)    */
  261.  
  262. int f;        /* default flag */
  263. int n;        /* # of repetitions wanted */
  264.  
  265. {
  266.     return(replaces(FALSE, f, n));
  267. }
  268.  
  269. qreplace(f, n)    /*    search and replace with query (ESC-CTRL-R)    */
  270.  
  271. int f;        /* default flag */
  272. int n;        /* # of repetitions wanted */
  273.  
  274. {
  275.     return(replaces(TRUE, f, n));
  276. }
  277.  
  278. /*    replaces:    search for a string and replace it with another
  279.             string. query might be enabled (according to
  280.             kind).                        */
  281. replaces(kind, f, n)
  282.  
  283. int kind;    /* Query enabled flag */
  284. int f;        /* default flag */
  285. int n;        /* # of repetitions wanted */
  286.  
  287. {
  288.     register int i;        /* loop index */
  289.     register int s;        /* success flag on pattern inputs */
  290.     register int slength,
  291.              rlength;    /* length of search and replace strings */
  292.     register int numsub;    /* number of substitutions */
  293.     register int nummatch;    /* number of found matches */
  294.     int nlflag;        /* last char of search string a <NL>? */
  295.     int nlrepl;        /* was a replace done on the last line? */
  296.     char tmpc;        /* temporary character */
  297.     char c;            /* input char for query */
  298.     char tpat[NPAT];    /* temporary to hold search pattern */
  299.     LINE *origline;        /* original "." position */
  300.     int origoff;        /* and offset (for . query option) */
  301.     LINE *lastline;        /* position of last replace and */
  302.     int lastoff;        /* offset (for 'u' query option) */
  303.  
  304.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  305.         return(rdonly());    /* we are in read only mode    */
  306.  
  307.     /* check for negative repititions */
  308.     if (f && n < 0)
  309.         return(FALSE);
  310.  
  311.     /* ask the user for the text of a pattern */
  312.     if ((s = readpattern(
  313.         (kind == FALSE ? "Replace" : "Query replace"))) != TRUE)
  314.         return(s);
  315.     strcpy(&tpat[0], &pat[0]);    /* salt it away */
  316.  
  317.     /* ask for the replacement string */
  318.     strcpy(&pat[0], &rpat[0]);    /* set up default string */
  319.     if ((s = readpattern("with")) != TRUE)
  320.         return(s);
  321.  
  322.     /* move everything to the right place and length them */
  323.     strcpy(&rpat[0], &pat[0]);
  324.     strcpy(&pat[0], &tpat[0]);
  325.     slength = strlen(&pat[0]);
  326.     rlength = strlen(&rpat[0]);
  327.  
  328.     /* set up flags so we can make sure not to do a recursive
  329.        replace on the last line */
  330.     nlflag = (pat[slength - 1] == '\n');
  331.     nlrepl = FALSE;
  332.  
  333.     if (kind) {
  334.         /* build query replace question string */
  335.         strcpy(tpat, "Replace '");
  336.         expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  337.         strcat(tpat, "' with '");
  338.         expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  339.         strcat(tpat, "'? ");
  340.  
  341.         /* initialize last replaced pointers */
  342.         lastline = NULL;
  343.         lastoff = 0;
  344.     }
  345.  
  346.     /* save original . position */
  347.     origline = curwp->w_dotp;
  348.     origoff = curwp->w_doto;
  349.  
  350.     /* scan through the file */
  351.     numsub = 0;
  352.     nummatch = 0;
  353.     while ((f == FALSE || n > nummatch) &&
  354.         (nlflag == FALSE || nlrepl == FALSE)) {
  355.  
  356.         /* search for the pattern */
  357.         if (forscan(&pat[0],PTBEG) != TRUE)
  358.             break;        /* all done */
  359.         ++nummatch;    /* increment # of matches */
  360.  
  361.         /* check if we are on the last line */
  362.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  363.         
  364.         /* check for query */
  365.         if (kind) {
  366.             /* get the query */
  367. pprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  368. qprompt:
  369.             update(FALSE);  /* show the proposed place to change */
  370.             c = (*term.t_getchar)();    /* and input */
  371.             mlwrite("");            /* and clear it */
  372.  
  373.             /* and respond appropriately */
  374.             switch (c) {
  375.                 case 'y':    /* yes, substitute */
  376.                 case ' ':
  377.                         break;
  378.  
  379.                 case 'n':    /* no, onword */
  380.                         forwchar(FALSE, 1);
  381.                         continue;
  382.  
  383.                 case '!':    /* yes/stop asking */
  384.                         kind = FALSE;
  385.                         break;
  386.  
  387.                 case 'u':    /* undo last and re-prompt */
  388.  
  389.             /* restore old position */
  390.             if (lastline == NULL) {
  391.                 /* there is nothing to undo */
  392.                 (*term.t_beep)();
  393.                 goto qprompt;
  394.             }
  395.             curwp->w_dotp = lastline;
  396.             curwp->w_doto = lastoff;
  397.             lastline = NULL;
  398.             lastoff = 0;
  399.  
  400.             /* delete the new string */
  401.             backchar(FALSE, rlength);
  402.             if (ldelete((long)rlength, FALSE) != TRUE) {
  403.                 mlwrite("ERROR while deleting");
  404.                 return(FALSE);
  405.             }
  406.  
  407.             /* and put in the old one */
  408.             for (i=0; i<slength; i++) {
  409.                 tmpc = pat[i];
  410.                 s = (tmpc == '\n' ? lnewline() :
  411.                             linsert(1, tmpc));
  412.                 if (s != TRUE) {
  413.                     /* error while inserting */
  414.                     mlwrite("Out of memory while inserting");
  415.                     return(FALSE);
  416.                 }
  417.             }
  418.  
  419.             --numsub;    /* one less substitutions */
  420.  
  421.             /* backup, and reprompt */
  422.             backchar(FALSE, slength);
  423.             goto pprompt;
  424.  
  425.                 case '.':    /* abort! and return */
  426.                         /* restore old position */
  427.                         curwp->w_dotp = origline;
  428.                         curwp->w_doto = origoff;
  429.                         curwp->w_flag |= WFMOVE;
  430.  
  431.                 case BELL:    /* abort! and stay */
  432.                         mlwrite("Aborted!");
  433.                         return(FALSE);
  434.  
  435.                 default:    /* bitch and beep */
  436.                         (*term.t_beep)();
  437.  
  438.                 case '?':    /* help me */
  439.                         mlwrite(
  440. "(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
  441.                         goto qprompt;
  442.  
  443.             }
  444.         }
  445.  
  446.         /* delete the sucker */
  447.         if (ldelete((long)slength, FALSE) != TRUE) {
  448.             /* error while deleting */
  449.             mlwrite("ERROR while deleteing");
  450.             return(FALSE);
  451.         }
  452.  
  453.         /* and insert its replacement */
  454.         for (i=0; i<rlength; i++) {
  455.             tmpc = rpat[i];
  456.             s = (tmpc == '\n' ? lnewline() : linsert(1, tmpc));
  457.             if (s != TRUE) {
  458.                 /* error while inserting */
  459.                 mlwrite("Out of memory while inserting");
  460.                 return(FALSE);
  461.             }
  462.         }
  463.  
  464.         /* save where we are if we might undo this... */
  465.         if (kind) {
  466.             lastline = curwp->w_dotp;
  467.             lastoff = curwp->w_doto;
  468.         }
  469.  
  470.         numsub++;    /* increment # of substitutions */
  471.     }
  472.  
  473.     /* and report the results */
  474.     mlwrite("%d substitutions",numsub);
  475.     return(TRUE);
  476. }
  477.  
  478. forscan(patrn,leavep)    /*    search forward for a <patrn>    */
  479.  
  480. char *patrn;        /* string to scan for */
  481. int leavep;        /* place to leave point
  482.                 PTBEG = beginning of match
  483.                 PTEND = at end of match        */
  484.  
  485. {
  486.     register LINE *curline;        /* current line during scan */
  487.     register int curoff;        /* position within current line */
  488.     register LINE *lastline;    /* last line position during scan */
  489.     register int lastoff;        /* position within last line */
  490.     register int c;            /* character at current position */
  491.     register LINE *matchline;    /* current line during matching */
  492.     register int matchoff;        /* position in matching line */
  493.     register char *patptr;        /* pointer into pattern */
  494.  
  495.     /* setup local scan pointers to global "." */
  496.  
  497.     curline = curwp->w_dotp;
  498.     curoff = curwp->w_doto;
  499.  
  500.     /* scan each character until we hit the head link record */
  501.  
  502.     while (curline != curbp->b_linep) {
  503.  
  504.         /* save the current position in case we need to
  505.            restore it on a match            */
  506.  
  507.         lastline = curline;
  508.         lastoff = curoff;
  509.  
  510.         /* get the current character resolving EOLs */
  511.  
  512.         if (curoff == llength(curline)) {    /* if at EOL */
  513.             curline = lforw(curline);    /* skip to next line */
  514.             curoff = 0;
  515.             c = '\n';            /* and return a <NL> */
  516.         } else
  517.             c = lgetc(curline, curoff++);    /* get the char */
  518.  
  519.         /* test it against first char in pattern */
  520.         if (eq(c, patrn[0]) != FALSE) {    /* if we find it..*/
  521.             /* setup match pointers */
  522.             matchline = curline;
  523.             matchoff = curoff;
  524.             patptr = &patrn[0];
  525.  
  526.             /* scan through patrn for a match */
  527.             while (*++patptr != 0) {
  528.                 /* advance all the pointers */
  529.                 if (matchoff == llength(matchline)) {
  530.                     /* advance past EOL */
  531.                     matchline = lforw(matchline);
  532.                     matchoff = 0;
  533.                     c = '\n';
  534.                 } else
  535.                     c = lgetc(matchline, matchoff++);
  536.  
  537.                 /* and test it against the pattern */
  538.                 if (eq(*patptr, c) == FALSE)
  539.                     goto fail;
  540.             }
  541.  
  542.             /* A SUCCESSFULL MATCH!!! */
  543.             /* reset the global "." pointers */
  544.             if (leavep == PTEND) {    /* at end of string */
  545.                 curwp->w_dotp = matchline;
  546.                 curwp->w_doto = matchoff;
  547.             } else {        /* at beginning of string */
  548.                 curwp->w_dotp = lastline;
  549.                 curwp->w_doto = lastoff;
  550.             }
  551.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  552.             return(TRUE);
  553.  
  554.         }
  555. fail:;            /* continue to search */
  556.     }
  557.  
  558.     /* we could not find a match */
  559.  
  560.     return(FALSE);
  561. }
  562.  
  563. /*     expandp:    expand control key sequences for output        */
  564.  
  565. expandp(srcstr, deststr, maxlength)
  566.  
  567. char *srcstr;    /* string to expand */
  568. char *deststr;    /* destination of expanded string */
  569. int maxlength;    /* maximum chars in destination */
  570.  
  571. {
  572.     char c;        /* current char to translate */
  573.  
  574.     /* scan through the string */
  575.     while ((c = *srcstr++) != 0) {
  576.         if (c == '\n') {        /* its an EOL */
  577.             *deststr++ = '<';
  578.             *deststr++ = 'N';
  579.             *deststr++ = 'L';
  580.             *deststr++ = '>';
  581.             maxlength -= 4;
  582.         } else if (c < 0x20 || c == 0x7f) {    /* control character */
  583.             *deststr++ = '^';
  584.             *deststr++ = c ^ 0x40;
  585.             maxlength -= 2;
  586.         } else if (c == '%') {
  587.             *deststr++ = '%';
  588.             *deststr++ = '%';
  589.             maxlength -= 2;
  590.  
  591.         } else {            /* any other character */
  592.             *deststr++ = c;
  593.             maxlength--;
  594.         }
  595.  
  596.         /* check for maxlength */
  597.         if (maxlength < 4) {
  598.             *deststr++ = '$';
  599.             *deststr = '\0';
  600.             return(FALSE);
  601.         }
  602.     }
  603.     *deststr = '\0';
  604.     return(TRUE);
  605. }
  606.